﻿using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Security;
using System.CodeDom.Compiler;
using System.Runtime.Serialization;
using ObjectBakery.Interfaces;

namespace ObjectBakery
{
    public class ScriptRunner : MarshalByRefObject
    {
        private Language language;

        private object ScriptInstance;

        private Type ScriptType;

        private IScriptObject MyEngine;

        private System.Security.PermissionSet ScriptPermissions;

        private System.Security.PermissionSet AllPermissions = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);

        public System.CodeDom.Compiler.CompilerErrorCollection CompilerErrors = new System.CodeDom.Compiler.CompilerErrorCollection();

        public IScriptObject ScriptEngine;

        public CompilerParameters compilerParameters;

        System.CodeDom.Compiler.CompilerResults Results;

        public ScriptRunner(Language language)
        {
            this.language = language;
            ScriptPermissions = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None);
            ScriptPermissions.AddPermission(new System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.Execution));

            #region COMPILER PARAMETERS
            compilerParameters = new System.CodeDom.Compiler.CompilerParameters();

            //TODO Pass in assemblies

            compilerParameters.ReferencedAssemblies.Add("mscorlib.dll");
            compilerParameters.ReferencedAssemblies.Add("System.dll");
            //CParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");

            //System.Runtime.Serializaton
            compilerParameters.ReferencedAssemblies.Add(typeof(DataContractAttribute).Assembly.Location);

            //System.Collections.Generic
            compilerParameters.ReferencedAssemblies.Add(typeof(List<string>).Assembly.Location);

            //Load local
            compilerParameters.ReferencedAssemblies.Add(this.GetType().Assembly.Location);


            //compilerParameters.ReferencedAssemblies.Add(System.Reflection.Assembly.GetAssembly(typeof(Script)).Location);

            compilerParameters.CompilerOptions = "/t:library";
            compilerParameters.GenerateInMemory = true;
            Object[] EvObj = { new System.Security.Policy.Zone(System.Security.SecurityZone.Internet) };
            compilerParameters.Evidence = new System.Security.Policy.Evidence(EvObj, null);
            #endregion

        }


        public void Load(string fullTypeName, string ScriptFile)
        {
            
            CodeDomProvider Provider = CodeDomProvider.CreateProvider(language.ToString());

            Results = Provider.CompileAssemblyFromFile(compilerParameters, ScriptFile);
            CompilerErrors = Results.Errors;
            if ((CompilerErrors.Count > 0))
            {
                throw new ApplicationException("There was an error compiling the script file.");
            }
            System.Reflection.Assembly ScriptAssembly;
            ScriptAssembly = Results.CompiledAssembly;
            ScriptPermissions.PermitOnly();



            ScriptInstance = ScriptAssembly.CreateInstance(fullTypeName);

            ScriptType = ScriptInstance.GetType();


            //object retVal = Invoke("GetEngine", null);
            AllPermissions.Demand();
            //ScriptEngine.ExecuteCalled += new System.EventHandler(this.HandleDoSomething);
        }

        public object Build(string fullTypeName, string scriptFile)
        {
            System.CodeDom.Compiler.CompilerParameters paramaters = new System.CodeDom.Compiler.CompilerParameters();

            paramaters.ReferencedAssemblies.Add("mscorlib.dll");
            paramaters.ReferencedAssemblies.Add("System.dll");
            //CParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");

            //System.Runtime.Serializaton
            paramaters.ReferencedAssemblies.Add(typeof(DataContractAttribute).Assembly.Location);


            //Load local
            paramaters.ReferencedAssemblies.Add(this.GetType().Assembly.Location);


            //paramaters.ReferencedAssemblies.Add(System.Reflection.Assembly.GetAssembly(typeof(Script)).Location);

            paramaters.CompilerOptions = "/t:library";
            paramaters.GenerateInMemory = true;
            Object[] EvObj = { new System.Security.Policy.Zone(System.Security.SecurityZone.Internet) };
            paramaters.Evidence = new System.Security.Policy.Evidence(EvObj, null);
            System.CodeDom.Compiler.CompilerResults Results;
            CodeDomProvider Provider = CodeDomProvider.CreateProvider(language.ToString());

            Results = Provider.CompileAssemblyFromFile(paramaters, scriptFile);
            CompilerErrors = Results.Errors;
            if ((CompilerErrors.Count > 0))
            {
                throw new ApplicationException("There was an error compiling the script file.");
            }
            System.Reflection.Assembly ScriptAssembly;
            ScriptAssembly = Results.CompiledAssembly;
            ScriptPermissions.PermitOnly();

            //ScriptInstance = ScriptAssembly.CreateInstance("Script");

            return ScriptAssembly.CreateInstance(fullTypeName);


        }

        public object GetProperty(string propertyName, object instance)
        {

            System.Reflection.PropertyInfo myProperty = instance.GetType().GetProperty(propertyName);
           return myProperty.GetValue(instance, null);

        }

         public void SetProperty(string propertyName, object value, object instance)
        {

            System.Reflection.PropertyInfo myProperty = instance.GetType().GetProperty(propertyName);
            myProperty.SetValue(instance, value, null);

        }

        /// <summary>
        /// This is a generic reflection method invoker.  
        /// It is generally used inside wrapper methods exposed by a subclassed version of this class.
        /// We do however expose it publicly.
        /// </summary>
        /// <param name="MethodName"></param>
        /// <param name="Parameters"></param>
        /// <returns></returns>
        public object Invoke(string methodName, object[] parameters)
        {
            //null protection
            if (parameters == null)
                parameters = new object[] { };

            System.Reflection.MethodInfo MyInfo = null;
            try { MyInfo = ScriptType.GetMethod(methodName); }
            catch { throw new Exception("Please Load a Type Before Invoking Methods..."); }
            ScriptPermissions.PermitOnly();
            try
            {
                //TODO expose outside.
                return MyInfo.Invoke(ScriptInstance, parameters);
            }
            catch (System.Security.SecurityException Ex)
            {
                // This should trap any permission exceptions caused by the script.
                return null;
            }
            AllPermissions.Demand();
        }

        /// <summary>
        /// Example of a Strongly-Typed Method wrapping a script call.  
        /// Child types of the runner will serve as extensible scripting domain interfaces.
        /// </summary>
        /// <param name="operand1"></param>
        /// <param name="operand2"></param>
        /// <returns></returns>
        public int RunMultiply(int operand1, int operand2)
        {
            object[] Params = { operand1, operand2 };
           return (int)Invoke("Multiply", Params);
        }

        public void HandleDoSomething(object sender, EventArgs e)
        {
            AllPermissions.Assert();
            Console.WriteLine("Event Handled");
            System.Security.PermissionSet.RevertAssert();
            ScriptPermissions.PermitOnly();
        }

    }


   
    

    public class LocalScriptObject : IScriptObject
    {


        #region IScriptObject Members

        public event EventHandler ExecuteCalled; //todo raise

        public object Execute()
        {

            return new object();
        }



        #endregion
    }
}
